﻿using System;
using System.Collections;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Net;
using System.Windows.Forms;
using System.ComponentModel;

[assembly: CLSCompliant(true)]
namespace DomExplorer
{
    /** blah blah blah some event handlers **/
    public delegate void WebBrowserDocumentCompletedEventHandler(Object sender, WebBrowserDocumentCompletedEventArgs e);
    public delegate void WebBrowserNavigatingEventHandler(Object sender, WebBrowserNavigatingEventArgs e);
  //  public override delegate void HtmlElementEventHandler(object sender, HtmlElementEventArgs e,CancelEventArgs cea);


    /** A delegate to handle adding nodes on its own thread **/
    /** NOTE: THESE ARE CURRENTLY NOT IN USE
    public delegate void AddTreeNodeToTreeView(TreeNode node);
    public delegate void AddTreeNodeToTreeNode(TreeNode parentNode, TreeNode childNode);
    **/

    /// <summary>
    /// I know that some of this can be extracted. go ahead, knock yourself out.
    /// </summary>
    /// 
    public partial class FormMain : Form
    {

        /** threading NOT IN USE
        private static AddTreeNodeToTreeView add;
        private static AddTreeNodeToTreeNode addNode;
         **/

        #region Sneaky private vars

        private static WebBrowser browser;
        private static TreeView TvDom;
        private RichTextBox source;
        private int previousNodetracker;
        string prevEleStyle;
        private static int nodeTracker;
        private bool inOverlay;

        StyleMainpulation imgStyle = new StyleMainpulation();
        StyleMainpulation linkStyle = new StyleMainpulation();
        StyleMainpulation formStyle = new StyleMainpulation();
        Label LblOverlay;

        #endregion

        public FormMain()
        {
            InitializeComponent();
        }

        private void FrmMain_Load(object sender, EventArgs e)
        {
            SetTvDom();
            CreateBrowser();
            browser.Navigate(Properties.Settings.Default.HomePage);
            TsAddressBar.Items.Add(Properties.Settings.Default.HomePage);
            TsAddressBar.SelectedIndex = 0;
            LoadFavorites();

        }

        private void TabNavigation_SelectedIndexChanged(object sender, EventArgs e)
        {
            RtbMore.Clear();
            if (TabNavigation.SelectedIndex == 2)
            {
                LvElements.Visible = true;
            }
            else
            {
                LvElements.Visible = false;
                PicSelected.Visible = false;
            }


        }

        #region Control Setup

        private void CreateBrowser()
        {
            /** Create the browser **/
            browser = new WebBrowser()
                {
                    /** set some properties to make things REALLY exciting **/
                    Dock = DockStyle.Fill,
                    ScrollBarsEnabled = true,

                };

            /** Add some event handlers **/
            browser.DocumentCompleted += new System.Windows.Forms.WebBrowserDocumentCompletedEventHandler(Browser_DocumentCompleted);
            browser.Navigating += new System.Windows.Forms.WebBrowserNavigatingEventHandler(Browser_Navigating);

            

            /** and the AddNode delegate
            add = new AddTreeNodeToTreeView(AddNodeToTree);
            addNode = new AddTreeNodeToTreeNode(AddTreeNode);
            **/

            /** This is more for later tabbed browsing **/
            var BrowserTab = new TabPage(Properties.Settings.Default.HomePage);
            var newIndex = TabBrowser.TabPages.Count + 1;
            TabBrowser.TabPages.Add(BrowserTab);

            LblOverlay = new Label()
            {
                Text = "In Overlay",
                Font = new Font("Arial",16,FontStyle.Bold),
                ForeColor = Color.Red,
                AutoSize = true            
            };

            /** add to tab and Increment the tab counter **/
            BrowserTab.Controls.Add(browser);
            BrowserTab.Controls.Add(LblOverlay);
            TabBrowser.TabPages[newIndex - 1].Focus();
            LblOverlay.Visible = false;



        }


        private void BrowserHtmlElement_Click(object sender,HtmlElementEventArgs e)
        {
            if (inOverlay)
            {
                browser.Stop();
                HtmlElement el = (HtmlElement)sender;
                RtbMore.Clear();
                RtbMore.Text = el.TagName + "\n" + el.OuterHtml;
                //el.Style = Properties.Settings.Default.DOMHighlightStyle;
                browser.AllowNavigation = false;
                LvSelectedNode.BackColor = Color.Bisque;
                DisplayElementInformation(LvSelectedNode, el);
                TabNavigation.SelectedTab = TabNavigation.TabPages["TPDoc"];
                LblMode.Text = "Overlay Selection";

                /** Using a bit of unmanaged to select the corrosponding node in tree **/
                mshtml.IHTMLElement  iDoc = (mshtml.IHTMLElement)el.DomElement;

                /** returns location in IHTMLDocument DOM **/
                int domIndex = iDoc.sourceIndex;

                /** go through all the nodes. The absolute DOM index is stored in a treenodes tag **/
                /** This was set when the "Parse" button was clicked **/
                TreeNodeCollection nodes = TvDom.Nodes;

                /** if the user didnt press "parse" first, there will be no nodes and we'll continue nicely **/
                foreach (TreeNode n in nodes)
                {
                    recursiveNodeSearch(n,domIndex);
                }
                TvDom.HideSelection = false;
            }
            else
            {
                browser.AllowNavigation = true;
                LblMode.Text = "Mode:";
            }
        }

        private void recursiveNodeSearch(TreeNode treeNode, int vv)
        {
            foreach (TreeNode tn in treeNode.Nodes)
            {
                /** check the tag of current node for index **/
                if ((int)tn.Tag == vv)
                {
                    /** match. select node and break **/
                    TvDom.SelectedNode = tn;
                    return;
                }
                recursiveNodeSearch(tn,vv);
            }
        }


        /// <summary>
        /// what do you think this does?
        /// </summary>
        private void ResetTrees()
        {
            nodeTracker = 0;
            previousNodetracker = 0;
            prevEleStyle = string.Empty;

            CtxCollapseDomTree.Enabled = false;
            CtxExpandDomTree.Enabled = false;

            TvDom.Nodes.Clear();
            TvLinks.Nodes.Clear();
            TvImages.Nodes.Clear();
            TvForms.Nodes.Clear();
            TvFrames.Nodes.Clear();
            LsvPageInfo.Items.Clear();
            RtbMore.Clear();

        }

        /// <summary>
        /// intended to set up generic richtext boxes
        /// </summary>
        /// <param name="theRtb"></param>
        private void SetSourceRtb(RichTextBox theRtb)
        {

            theRtb.Multiline = true;
            theRtb.DetectUrls = true;
            theRtb.ScrollBars = RichTextBoxScrollBars.Both;
            theRtb.ShortcutsEnabled = true;
            theRtb.WordWrap = false;
            theRtb.Dock = DockStyle.Fill;
            theRtb.ReadOnly = true;
            theRtb.BackColor = System.Drawing.Color.White;

            theRtb.Font = Properties.Settings.Default.SourceViewerFont;
        }

        /// <summary>
        /// Creates and adds menu items to the source viewer RichTextBox
        /// </summary>
        /// <param name="ctx">Context menu for source view rtb</param>
        /// <returns>completed context menu</returns>
        private ContextMenuStrip SetSourceViewerContext(ContextMenuStrip ctx)
        {

            /** Create the items **/
            var CtxSrcSelectAll = new ToolStripMenuItem("Select All");
            var CtxSrcWordWrap = new ToolStripMenuItem("Word Wrap")
                {
                    CheckOnClick = true
                };

            var CtxSrcCopy = new ToolStripMenuItem("Copy")
                {
                    ToolTipText = "Copy selected text",
                    Enabled = false
                };
            var CtxFind = new ToolStripMenuItem("Search Source")
            {
                ToolTipText = "Search source code",
                Enabled = false
            };

            var CtxCloseViewer = new ToolStripMenuItem("Close Tab")
                {
                    ToolTipText = "Close this tab"
                };


            /** Event handlers **/
            ctx.Opening += new System.ComponentModel.CancelEventHandler(CtxSrc_Opening);
            CtxSrcSelectAll.Click += new System.EventHandler(CtxSrcSelectAll_Click);
            CtxSrcWordWrap.Click += new System.EventHandler(CtxSrcWrap_Click);
            CtxSrcCopy.Click += new System.EventHandler(CtxSrcCopy_Click);
            CtxCloseViewer.Click += new EventHandler(CtxCloseViewer_Click);

            /** add the items **/
            ctx.Items.Add(CtxSrcSelectAll);
            ctx.Items.Add(CtxSrcWordWrap);
            ctx.Items.Add(CtxSrcCopy);
            ctx.Items.Add(CtxCloseViewer);

            return ctx;
        }


        /// <summary>
        /// Sets up the DOM treeview
        /// </summary>
        /// <remarks>why all this? because the generated code doesnt like static members and threads dont like non-static members</remarks>
        private void SetTvDom()
        {
            TvDom = new TreeView()
                {
                    Dock = System.Windows.Forms.DockStyle.Fill,
                    HideSelection = false,
                    //ImageList = imageList1,
                    Indent = 2,
                    Location = new System.Drawing.Point(3, 3),
                    Name = "TvDom",
                    Scrollable = true,
                    Size = new System.Drawing.Size(183, 507),
                    TabIndex = 0
                };

            TvDom.AfterSelect += new System.Windows.Forms.TreeViewEventHandler(this.TvDom_AfterSelect);
            this.TpDoc.Controls.Add(TvDom);
            TvDom.BringToFront();
        }

        #endregion


        #region DOM Processing

        /// <summary>
        /// Begins the process of the browser document from the root "html" node. also triggers parsing of links, images and forms
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void TsParseDom_Click(object sender, EventArgs e)
        {
            /** this has GOT to be the cheesiest way to handle this condition, but i'll llok at it later **/
            //TODO guard parse button against unfinished page load
            if (TsMoving.Visible)
            {
                return;
            }

            ResetTrees();
            TabElements.SelectedTab = TabNavigation.TabPages[0];
            TabElements.SelectedIndex = 0;
            TvDom.BeginUpdate();

            /** get der dokument **/
            HtmlElementCollection elemColl = browser.Document.GetElementsByTagName("HTML");

            DoStuff(elemColl);

            /** load up the trees baby! **/
            ProcessPageInfo();
            ProcessLinks();
            PrintImages();
            ProcessForms();
            ProcessFrames();

            /** update the status **/
            CtxCollapseDomTree.Enabled = true;
            CtxExpandDomTree.Enabled = false;
            TvDom.ExpandAll();
            TvDom.EndUpdate();          

        }

        /// <summary>
        /// Called by the thread.start to set up for filling the DOM tree
        /// </summary>
        /// <param name="elemColl">HTMLElement collection with the "HTML" tag as its root</param>
        /// <remarks>Has to be passed as an object for the ParameterizedThreadStart</remarks>
        private void DoStuff(HtmlElementCollection elemColl)
        {
            var rootNode = new TreeNode(browser.Document.Domain)
                {
                    Tag = -1
                };
            TvDom.Nodes.Add(rootNode);
            FillDomTree(elemColl, rootNode);
        }

        /// <summary>
        /// Recursive function to add the DOM element to the treeview
        /// </summary>
        /// <param name="currentEle">collection of elements</param>
        /// <param name="tn">tree node. currently not used</param>
        /// <remarks>The treenode was used prior to placing this function on its own thread
        /// at that point, I was nesting the nodes. right now, i am having trouble doing this across threads</remarks>
        private static void FillDomTree(HtmlElementCollection currentEle, TreeNode tn)
        {

            foreach (HtmlElement element in currentEle)
            {
                TreeNode tempNode;

                var tag = element.TagName;

                if (tag.CompareTo("!") == 0)
                {
                    tempNode = tn.Nodes.Add("<Comment>");
                }
                else
                {

                    tempNode = tn.Nodes.Add("<" + element.TagName + ">");
                }

                /** a counter to keep track of the absolute index of the nodes. Stuffing value into tag **/
                nodeTracker += 1;
                tempNode.Tag = nodeTracker;
                //tempNode.ImageIndex = 1;              

                /** over and over and over and over **/
                FillDomTree(element.Children, tempNode);

            }

        }

        private void OverlayPage()
        {
            //HtmlElement hel = browser.Document.CreateElement("ins");
            //hel.SetAttribute("style", "background-color: #dddddd");

            //hel.Id = "dexIm";

            //hel.InnerHtml = "&LT" + element.TagName + "&GT";
            //hel = browser.Document.All[element.Id].InsertAdjacentElement(HtmlElementInsertionOrientation.BeforeBegin,hel);

            //Button bt = new Button();
            //bt.AutoSize = true;
            //bt.AutoSizeMode = AutoSizeMode.GrowAndShrink;
            //bt.BackColor = Color.Pink;
            //bt.FlatStyle = FlatStyle.Flat;
            //// bt.Width = 25;
            //bt.Text = element.TagName;
            //int x = element.OffsetRectangle.Location.X + 2;
            //int y = element.OffsetRectangle.Location.Y + 2;
            //bt.Left = x;
            //bt.Top = y;

            //browser.Controls.Add(bt);
        }

        /// <summary>
        /// Tries to go through all the forms in a page. for whatever
        /// reason, if the "input" tags are nested, they wont be pulled out of the 
        /// Forms collection. so, we must hack
        /// </summary>
        private void ProcessForms()
        {

            if (browser.Document.Forms.Count == 0)
            {
                TpForms.Hide();
                return;
            }

            TpForms.Show();
            foreach (HtmlElement item in browser.Document.Forms)
            {
                var tn = new TreeNode("Form Name: " + item.Name)
                    {
                        Tag = item.OuterHtml
                    };

                HtmlElementCollection inColl = item.GetElementsByTagName("input");

                for (int i = 0; i < inColl.Count - 1; i++)
                {
                    tn.Nodes.Add("<Input>");
                }

                TvForms.Nodes.Add(tn);
            }
        }

        private void ProcessFrames()
        {
            var treeN = new TreeNode("Frames: " + browser.Document.Window.Frames.Count.ToString());


            if (browser.Document.Window.Frames.Count > 0)
            {

                foreach (HtmlWindow frame in browser.Document.Window.Frames)
                {
                    try
                    {
                        var tn = new TreeNode(frame.Url.ToString())
                        {
                            Tag = frame.Url.ToString()
                        };
                        treeN.Nodes.Add(tn);
                    }
                    catch (UnauthorizedAccessException)
                    {
                        var tn = new TreeNode("Blocked")
                        {
                            Tag = "Blocked"
                        };
                        treeN.Nodes.Add(tn);
                    }

                }
            }


            TvFrames.Nodes.Add(treeN);
        }

        /// <summary>
        /// extracts stuff from the Images collection
        /// </summary>
        private void PrintImages()
        {
            ProcessElements("src", TvImages, browser.Document.Images);
        }

        /// <summary>
        /// In charge of processing all anchor tags in page and filling TreeView
        /// </summary>
        private void ProcessLinks()
        {
            //TODO remove this - no need to seperate out at this point
            ProcessElements("href", TvLinks, browser.Document.Links);
        }

        /// <summary>
        /// Gets general information about the loaded webpage and adds elements to the listview
        /// </summary>
        private void ProcessPageInfo()
        {
            IPAddress[] ips = Dns.GetHostAddresses(browser.Document.Url.DnsSafeHost);


            foreach (DictionaryEntry de in ElementAccess.ProcessPageInfo(browser.Document))
            {
                AddListviewItem(de.Key.ToString(), de.Value.ToString());
            }

            var lvi = new ListViewItem("IP Addresses:");
            lvi.SubItems.Add(ips[0].ToString());
            LsvPageInfo.Items.Add(lvi);

            for (int i = 1; i < ips.Length - 1; i++)
            {
                lvi = new ListViewItem("");
                lvi.SubItems.Add(ips[i].ToString());
                LsvPageInfo.Items.Add(lvi);
            }

        }

        /// <summary>
        /// used to add ip addresses to the listview
        /// </summary>
        /// <param name="mainText">text of the main item</param>
        /// <param name="subItemText">text of the sub item</param>
        private void AddListviewItem(string mainText, string subItemText)
        {
            ListViewItem lvi = new ListViewItem(mainText);
            lvi.SubItems.Add(subItemText);
            LsvPageInfo.Items.Add(lvi);
        }

        /// <summary>
        /// Loops through to fill in treeview with html tag values
        /// </summary>
        /// <param name="tagName">tag to evaluate</param>
        /// <param name="tv">TreeView to add nodes to</param>
        /// <param name="htec">Collection of HTML Elements to loop through</param>
        private static void ProcessElements(string tagName, TreeView tv, HtmlElementCollection htec)
        {

            foreach (HtmlElement item in htec)
            {
                var txtNde = new TreeNode();
                var ndeString = item.GetAttribute(tagName);
                string fileName;
                if (tagName == "src")
                {
                    try
                    {

                        fileName = Path.GetFileName(ndeString);
                        txtNde.Text = fileName;
                    }
                    catch (ArgumentException)
                    {

                        txtNde.Text = ndeString;
                    }

                }
                else
                {
                    if (item.InnerHtml != null)
                    {
                        txtNde.Text = item.InnerHtml;
                    }
                    else
                    {
                        txtNde.Text = "*No associated text*";
                    }

                }

                txtNde.Tag = ndeString;
                tv.Nodes.Add(txtNde);
            }
        }

        #endregion


        #region Treeview Handlers

        /// <summary>
        /// Handles highlighting selected area based on node in DOM tree
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void TvDom_AfterSelect(object sender, TreeViewEventArgs e)
        {

            if ((int)TvDom.SelectedNode.Tag == -1)
                return;

            LblMode.Text = "DOM Tree Selection";
            LvSelectedNode.BackColor = SystemColors.Window;
            TvDom.HideSelection = false;

            try
            {
                var selectedNodeIndex = (int)TvDom.SelectedNode.Tag;
                RtbMore.Clear();
                RtbMore.Text = browser.Document.All[selectedNodeIndex].OuterHtml;

                browser.Document.All[previousNodetracker].Style = prevEleStyle;
                prevEleStyle = browser.Document.All[selectedNodeIndex].Style;

                browser.Document.All[selectedNodeIndex].Style = Properties.Settings.Default.DOMHighlightStyle;
                browser.Document.All[selectedNodeIndex].ScrollIntoView(true);

                DisplayElementInformation(LvSelectedNode, browser.Document.All[selectedNodeIndex]);

            }
            catch (NullReferenceException)
            {
                RtbMore.Text = "No associated text";
            }

            int x = (int)TvDom.SelectedNode.Tag;
            if (TvDom.SelectedNode != null)
            {
                previousNodetracker = (int)TvDom.SelectedNode.Tag;
            }

        }

        /// <summary>
        /// This takes care of displaying input element info
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void TvForms_AfterSelect(object sender, TreeViewEventArgs e)
        {
            var prevEleStyle = string.Empty;

            if (e.Node.Level == 0)
            {
                RtbMore.Clear();
                RtbMore.Text = browser.Document.Forms[TvForms.SelectedNode.Index].OuterHtml;
            }
            else
            {
                /** Set the info for the selected INPUT element **/
                HtmlElementCollection coll = browser.Document.GetElementsByTagName("input");

                SetFormsText(coll);

                try
                {
                    coll[TvForms.SelectedNode.Index].Focus();
                }
                catch (System.NotSupportedException)
                {
                    RtbMore.Text += "\n" + Properties.Settings.Default.HiddenElementMessage + "\n";
                }


                formStyle.CurrentIndex = TvForms.SelectedNode.Index;
                formStyle.PreviousStyle = coll[TvForms.SelectedNode.Index].Style;
                formStyle.CurrentStyle = Properties.Settings.Default.FormHighlightStyle;
                formStyle.SetElementStyle(coll, TvForms.SelectedNode.Index);
            }
        }

        /// <summary>
        /// Used to set put together the RTB text for form elements
        /// </summary>
        /// <param name="coll">browser documents forms collection</param>
        private void SetFormsText(HtmlElementCollection coll)
        {
            RtbMore.Clear();
            LvElements.Items.Clear();

            foreach (DictionaryEntry de in ElementAccess.GetElementInfo(coll[TvForms.SelectedNode.Index]))
            {
                var lvi = new ListViewItem(de.Key.ToString());
                lvi.SubItems.Add(de.Value.ToString());
                LvElements.Items.Add(lvi);
            }

            RtbMore.Text += coll[TvForms.SelectedNode.Index].OuterHtml;

        }

        /// <summary>
        /// handles providing information about selected frame element.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        /// <remarks>Still a bit shakey</remarks>
        private void TvFrames_AfterSelect(object sender, TreeViewEventArgs e)
        {
            RtbMore.Clear();
            LvElements.Items.Clear();
            RtbMore.BringToFront();
            PicSelected.Visible = false;

            if (TvFrames.Nodes.Count >= 1)
            {
                if (TvFrames.SelectedNode.Level > 0)
                {
                    if (TvFrames.SelectedNode.Tag.ToString() == "Blocked")
                    {
                        RtbMore.Text = "Access to this frame has been blocked";
                        return;
                    }

                    DisplayElementInformation(LvElements, browser.Document.Window.Frames[TvFrames.SelectedNode.Index].WindowFrameElement);

                    RtbMore.Text += browser.Document.Window.Frames[TvFrames.SelectedNode.Index].WindowFrameElement.OuterHtml + "\n";
                }
            }

        }

        /// <summary>
        /// Loads the information and style for selected image
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void TvImages_AfterSelect(object sender, TreeViewEventArgs e)
        {

            /** Clear the rich text area of all offending text **/
            RtbMore.Clear();
            LvElements.Items.Clear();
            PicSelected.Visible = true;
            RtbMore.BringToFront();

            Image img = DOMImages.ProcessSelectedImage(browser.Document.Images[TvImages.SelectedNode.Index]);

            if (img != null)
            {
                PicSelected.Image = DOMImages.GetImageThumbnail(img, PicSelected.Size);
            }
            else
            {
                PicSelected.Image = PicSelected.ErrorImage;
            }

            /** try our best to focus. if we cant, that must mean the element is hidden behind a tab or something **/
            try
            {
                browser.Document.Images[TvImages.SelectedNode.Index].Focus();
            }
            catch (System.NotSupportedException)
            {
                RtbMore.Text += "\n" + Properties.Settings.Default.HiddenElementMessage + "\n";
            }

            foreach (DictionaryEntry de in ElementAccess.GetElementInfo(browser.Document.Images[TvImages.SelectedNode.Index]))
            {
                var lvi = new ListViewItem(de.Key.ToString());
                lvi.SubItems.Add(de.Value.ToString());
                LvElements.Items.Add(lvi);
            }

            RtbMore.Text += browser.Document.Images[TvImages.SelectedNode.Index].OuterHtml + "\n";

            imgStyle.PreviousStyle = browser.Document.Images[TvImages.SelectedNode.Index].Style;
            imgStyle.CurrentStyle = Properties.Settings.Default.ImageHighlightStyle;
            imgStyle.PreviousIndex = TvImages.SelectedNode.Index;
            imgStyle.SetElementStyle(browser.Document.Images[TvImages.SelectedNode.Index]);

        }

        /// <summary>
        /// Occurs when user selects a links node. fills in info and highlights link on pages using default sylesheet
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void TvLinks_AfterSelect(object sender, TreeViewEventArgs e)
        {

            RtbMore.Clear();
            RtbMore.BringToFront();
            PicSelected.Visible = false;

            try
            {
                browser.Document.Links[TvLinks.SelectedNode.Index].Focus();
            }
            catch (System.NotSupportedException)
            {
                RtbMore.Text += Properties.Settings.Default.HiddenElementMessage + "\n";
            }

            DisplayElementInformation(LvElements, browser.Document.Links[TvLinks.SelectedNode.Index]);

            RtbMore.Text += browser.Document.Links[TvLinks.SelectedNode.Index].OuterHtml + "\n";

            linkStyle.CurrentIndex = TvLinks.SelectedNode.Index;
            linkStyle.PreviousStyle = browser.Document.Links[TvLinks.SelectedNode.Index].Style;
            linkStyle.CurrentStyle = Properties.Settings.Default.LinkHighlightStyle;
            linkStyle.SetElementStyle(browser.Document.Links[TvLinks.SelectedNode.Index]);

        }

        private void DisplayElementInformation(ListView theListView, HtmlElement theElement)
        {
            theListView.Items.Clear();
            var itemInfo = new Hashtable();
            itemInfo = ElementAccess.GetElementInfo(theElement);
            foreach (DictionaryEntry de in itemInfo)
            {
                var lvi = new ListViewItem(de.Key.ToString());
                lvi.SubItems.Add(de.Value.ToString());
                lvi.ToolTipText = de.Value.ToString();
                theListView.Items.Add(lvi);
            }

            theListView.ShowItemToolTips = true;

        }

        private void CtxInvoke_Click(object sender, EventArgs e)
        {
            var selectedNodeIndex = (int)TvDom.SelectedNode.Tag;
            string verb = string.Empty;
            HtmlElement webControl = browser.Document.All[selectedNodeIndex];
            Clipboard.Clear();

            if (webControl.TagName.Equals("INPUT", StringComparison.InvariantCultureIgnoreCase))
            {

                string val = webControl.GetAttribute("type");

                if (string.Compare(val, "submit", true) == 0)
                {
                    webControl.InvokeMember("Click");
                    verb = "Click()";
                }
                if (string.Compare(val, "text", true) == 0)
                {
                    string va = CtxValue.Text;
                    webControl.SetAttribute("value", va);
                    webControl.InvokeMember("onkeydown");
                }


            }

            if (webControl.TagName.Equals("a", StringComparison.InvariantCultureIgnoreCase))
            {
                webControl.InvokeMember("Click");
                verb = "Click()";
            }

            string clipString = "ieWindowHandle.document.getElementById('" + webControl.Id + "')." + verb;
            Clipboard.SetText(clipString);
        }

        /// <summary>
        /// handles enabling/disabling context menu items based on node type
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void CtxDomTree_Opening(object sender, System.ComponentModel.CancelEventArgs e)
        {

            /** first off, make sure of whether a node, or just the tree was targeted **/
            if (TvDom.SelectedNode == null || (int)TvDom.SelectedNode.Tag == -1)
            {
                CtxSet.Enabled = false;
                CtxInvoke.Enabled = false;
                return;
            }

            /** ok, cool. now figure out which element was selected **/
            var selectedNodeIndex = (int)TvDom.SelectedNode.Tag;
            HtmlElement webControl = browser.Document.All[selectedNodeIndex];

            /** get the tag **/
            string tag = webControl.TagName;

            /** handle elements that accept an input event **/
            if (string.Compare(tag, "input", true) == 0 || string.Compare(tag, "a", true) == 0)
            {
                string typ = webControl.GetAttribute("type");

                /** handle text controls **/
                if (string.Compare(typ, "text", true) == 0)
                {
                    CtxSet.Enabled = true;
                    CtxInvoke.Enabled = false;

                }
                /** others **/
                else
                {
                    CtxSet.Enabled = false;
                    CtxInvoke.Enabled = true;
                }

            }
            else
            {
                CtxInvoke.Enabled = false;
            }

        }

        /// <summary>
        /// handles setting value for text
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void CtxValue_TextChanged(object sender, EventArgs e)
        {
            /** If theres a value ontered into the textbox enable the invoke option to send the text **/
            if (CtxValue.Text.Length > 0)
            {
                CtxInvoke.Enabled = true;
            }
        }

        #endregion


        #region DOM Notes RTB

        /// <summary>
        /// enables/disables word wrap in the box. off by default
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ctxWordWrap_Click(object sender, EventArgs e)
        {
            RtbMore.WordWrap = !RtbMore.WordWrap;

            CtxWordWrap.Checked = RtbMore.WordWrap;
        }

        /// <summary>
        /// Select all text
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ctxSelectAll_Click(object sender, EventArgs e)
        {
            RtbMore.SelectAll();
        }

        /// <summary>
        /// Copy selected text to clipboard
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        /// <remarks>This is usually part of default context menu, but that changes when set to ReadOnly, apparently</remarks>
        private void ctxCopy_Click(object sender, EventArgs e)
        {
            Clipboard.SetText(RtbMore.SelectedText);
        }

        /// <summary>
        /// If theres a url in the RTB, this'll handle a click event
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void RtbMore_LinkClicked(object sender, LinkClickedEventArgs e)
        {
            Process.Start(e.LinkText);
        }

        private void RtbMore_SelectionChanged(object sender, EventArgs e)
        {
            CtxCopy.Enabled = RtbMore.SelectionLength > 0;
        }

        #endregion


        #region Toolstrip and Context Menu

        private void CtxCollapseDomTree_Click(object sender, EventArgs e)
        {
            TvDom.CollapseAll();
            CtxExpandDomTree.Enabled = true;
            CtxCollapseDomTree.Enabled = false;
        }

        private void CtxExpandDomTree_Click(object sender, EventArgs e)
        {
            TvDom.BeginUpdate();
            TvDom.ExpandAll();
            TvDom.EndUpdate();
            TvDom.Nodes[0].EnsureVisible();
            CtxExpandDomTree.Enabled = false;
            CtxCollapseDomTree.Enabled = true;
        }

        /// <summary>
        /// checks to see if the connected source rtb instance has selected text
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void CtxSrc_Opening(object sender, System.ComponentModel.CancelEventArgs e)
        {
            /** Get this stuff usable **/
            var cms = (ContextMenuStrip)sender;
            var rtb = (RichTextBox)cms.SourceControl;

            /** magic number 2 = CtxSrcCopy **/
            cms.Items[2].Enabled = rtb.SelectedText.Length > 0;

        }

        void CtxCloseViewer_Click(object sender, EventArgs e)
        {
            var cms = (ToolStripMenuItem)sender;
            var rtb = GetSourceRTB(cms);
            var tb = (TabPage)rtb.Parent;

            /** Seems like a lot of work to get rid of the right page... **/
            TabBrowser.TabPages.Remove(tb);

        }

        private void CtxSrcCopy_Click(object sender, EventArgs e)
        {
            var cms = (ToolStripMenuItem)sender;
            var rtb = GetSourceRTB(cms);

            Clipboard.SetText(rtb.SelectedText);
        }

        private void CtxSrcSelectAll_Click(object sender, EventArgs e)
        {
            var cms = (ToolStripMenuItem)sender;
            var rtb = GetSourceRTB(cms);
            rtb.SelectAll();
        }

        private void CtxSrcWrap_Click(object sender, EventArgs e)
        {
            var cms = (ToolStripMenuItem)sender;
            var rtb = GetSourceRTB(cms);

            /** Turn word wrap on/off and set the indicator **/
            rtb.WordWrap = !rtb.WordWrap;
            cms.Checked = rtb.WordWrap;

        }

        /// <summary>
        /// used to get the RichTextBox that a particular context menu item is referring to
        /// </summary>
        /// <param name="cmi">the context menu item</param>
        /// <returns>the target RichTextBox</returns>
        private RichTextBox GetSourceRTB(ToolStripMenuItem cmi)
        {
            var ts = (ContextMenuStrip)cmi.Owner;
            var rtb = (RichTextBox)ts.SourceControl;

            return rtb;
        }

        /// <summary>
        /// handles the enter key
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void TsAddressBar_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Enter)
            {
                browser.Navigate(TsAddressBar.Text);
            }
        }

        /// <summary>
        /// launches the favorites item
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void TsFavorites_DropDownItemClicked(object sender, ToolStripItemClickedEventArgs e)
        {
            browser.Navigate(e.ClickedItem.Tag.ToString());
        }

        /// <summary>
        /// Goes through users IE faves and loads them in.
        /// <remarks>Doesnt really account for separations by directory, just presents 'em all in big ole list</remarks>
        /// </summary>
        private void LoadFavorites()
        {
            string[] folders = Directory.GetDirectories(Environment.GetFolderPath(Environment.SpecialFolder.Favorites));
            var lst = new SortedList();
            string line = string.Empty;
            char[] delimiters = { '=' };

            for (int x = 0; x < folders.Length - 1; x++)
            {
                var di = new DirectoryInfo(folders[x]);

                if (di.GetFiles().Length > 0)
                {
                    string[] fl = Directory.GetFiles(folders[x]);

                    line = ParseIEFavorites(lst, line, delimiters, fl);
                }
            }

            /** Im sure theres a better way to sort, but I'm doing it this way **/
            /** cause I WANT TO..... I'm Bad.... I'm Nation-Wide **/
            for (int i = 0; i < lst.Count - 1; i++)
            {
                ToolStripMenuItem tsc = new ToolStripMenuItem()
                {
                    Text = lst.GetKey(i).ToString(),
                    Tag = lst.GetByIndex(i)
                };

                TsFavorites.DropDownItems.Add(tsc);

            }

        }

        private static string ParseIEFavorites(SortedList lst, string line, char[] delimiters, string[] fl)
        {
            for (int i = 0; i < fl.Length; i++)
            {
                var sr = new StreamReader(fl[i]);
                line = sr.ReadLine();

                while (line != null)
                {
                    /** the .url files are like ini's with a url section **/
                    if (line.StartsWith("url", StringComparison.OrdinalIgnoreCase))
                    {
                        string[] url = line.Split(delimiters);
                        lst.Add(Path.GetFileNameWithoutExtension(fl[i]), url[1]);
                        break;
                    }
                    line = sr.ReadLine();
                }

            }
            return line;
        }

        /// <summary>
        /// creates a new tab pages, then adds a browser control with all the amenities. all for only $1
        /// </summary>
        /// <param name="sender">object type stuff</param>
        /// <param name="e">event args...whod of thunk</param>
        private void TsViewSource_Click(object sender, EventArgs e)
        {
            /** new tab **/
            var sourceTab = new TabPage("Source View (" + browser.Document.Domain + ")");

            source = new RichTextBox();

            /** set the default properties **/
            SetSourceRtb(source);
            /** add it to the tab **/
            sourceTab.Controls.Add(source);

            /** write source **/
            foreach (HtmlElement pageElement in browser.Document.All)
            {
                source.Text += pageElement.OuterHtml;
            }

            /** add tab to window **/
            TabBrowser.TabPages.Add(sourceTab);

            /** create context menu **/
            var CtxSrc = new ContextMenuStrip();
            SetSourceViewerContext(CtxSrc);
            this.source.ContextMenuStrip = CtxSrc;
            TabBrowser.SelectedTab = sourceTab;
        }

        private void TsOverlay_Click(object sender, EventArgs e)
        {
            if (TsOverlay.Checked)
            {
                inOverlay = true;
                TabBrowser.TabPages[0].Text = browser.Document.Title + " (Overlay Mode)";
                LblOverlay.Left = (browser.Width / 2) - (LblOverlay.Width / 2);
                LblOverlay.Top = browser.Height - LblOverlay.Height;
                LblOverlay.Visible = true;
                LblOverlay.BringToFront();
            }
            else
            {
                inOverlay = false;
                TabBrowser.TabPages[0].Text = browser.Document.Title;
                LblOverlay.Visible = false;
                LblOverlay.SendToBack();
            }
        }

        #endregion


        #region Browser Nav

        /// <summary>
        /// navigates -1
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void TsBack_Click(object sender, EventArgs e)
        {
            browser.GoBack();
            TsBrowserStatus.Text = browser.StatusText;
            TabBrowser.SelectedTab.Text = browser.Document.Title;
            TsAddressBar.Text = browser.Url.ToString();
        }

        private void TSFwd_Click(object sender, EventArgs e)
        {

            browser.GoForward();
            TsBrowserStatus.Text = browser.StatusText;
            TabBrowser.SelectedTab.Text = browser.Document.Title;
            TsAddressBar.Text = browser.Url.ToString();
        }

        private void TsGo_Click(object sender, EventArgs e)
        {
            TabBrowser.SelectedTab.Text = browser.Url.ToString();
            browser.Navigate(TsAddressBar.Text);
        }

        private void TsGoHome_Click(object sender, EventArgs e)
        {
            browser.Navigate(Properties.Settings.Default.HomePage);
        }

        private void TsRefresh_Click(object sender, EventArgs e)
        {
            browser.Refresh();
        }

        private void TsStopPage_Click(object sender, EventArgs e)
        {
            browser.Stop();
            TsMoving.Visible = false;
            TsStopPage.Enabled = false;
            TsRefresh.Enabled = true;
        }

        /// <summary>
        /// Once the doc is loaded, we take care of some button enables and status text
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Browser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
        {
            /** make sure we are only handling the event sent by the main url, not frames, etc **/
            if (!e.Url.AbsoluteUri.Equals(browser.Document.Url.AbsoluteUri))
            {
                return;
            }

            TsBrowserStatus.Text = "Complete";
            TsAddressBar.Text = browser.Document.Url.AbsoluteUri;
            TabBrowser.TabPages[0].Text = browser.Document.Title;
            
            /** set the buttons **/
            TsBack.Enabled = browser.CanGoBack;
            TSFwd.Enabled = browser.CanGoForward;
            TsStopPage.Enabled = false;
            TsRefresh.Enabled = true;
            TsViewSource.Enabled = true;
            TsParseDom.Enabled = true;
            TsMoving.Visible = false;
            TsMoving.Visible = false;
            TabBrowser.SelectedIndex = 0;

            HtmlElementCollection ev = browser.Document.Images;
            foreach (HtmlElement item in ev )
            {

                item.Click += new HtmlElementEventHandler(BrowserHtmlElement_Click); 
                                 
            }
            HtmlElementCollection et = browser.Document.Links;
            foreach (HtmlElement item in et)
            {

                item.Click += new HtmlElementEventHandler(BrowserHtmlElement_Click);

            }
            HtmlElementCollection ei = browser.Document.GetElementsByTagName("Input");
            foreach (HtmlElement item in ei)
            {

                item.Click += new HtmlElementEventHandler(BrowserHtmlElement_Click);
                
            }
        }

        /// <summary>
        /// using this to invalidate DOM trees
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Browser_Navigating(Object sender, WebBrowserNavigatingEventArgs e)
        {
            ResetTrees();
            PicSelected.Image = PicSelected.InitialImage;
            TsViewSource.Enabled = false;
            TsMoving.Visible = true;
            TsMoving.Visible = true;
            TsParseDom.Enabled = false;
            TsStopPage.Enabled = true;
            TsRefresh.Enabled = false;
            string[] smartA = { "On my way...", "Zug Zug", "Yes, Master", "Hold On...", "Wow, you're bossy!", "Loading", "Cmon!", "Alright, already" };

            Random rand = new Random();
            TsBrowserStatus.Text = smartA[rand.Next(0, 7)];
        }

        /// <summary>
        /// this will just return the final IP of the page
        /// </summary>
        /// <param name="b"></param>
        /// <returns></returns>
        private string GetIpAddress(WebBrowser b)
        {
            var retVal = string.Empty;
            IPAddress[] ips = Dns.GetHostAddresses(b.Document.Url.DnsSafeHost);

            foreach (var add in ips)
            {
                retVal = add.ToString();
            }

            return retVal;

        }

        #endregion


        #region Element Information Listview

        private void LvSelectedNode_ItemSelectionChanged(object sender, ListViewItemSelectionChangedEventArgs e)
        {
            CtxCopyItemText.Enabled = LvSelectedNode.SelectedItems.Count > 0;
            CtxCopyValue.Enabled = LvSelectedNode.SelectedItems.Count > 0;

        }

        private void CtxCopyValue_Click(object sender, EventArgs e)
        {
            var cms = (ToolStripMenuItem)sender;
            var ts = (ContextMenuStrip)cms.Owner;
            var rtb = (ListView)ts.SourceControl;
            Clipboard.SetText(rtb.SelectedItems[0].SubItems[1].Text);
        }

        private void CtxCopyItemText_Click(object sender, EventArgs e)
        {
            var cms = (ToolStripMenuItem)sender;
            var ts = (ContextMenuStrip)cms.Owner;
            var rtb = (ListView)ts.SourceControl;

            var returnString = rtb.SelectedItems[0].SubItems[0].Text + ": " + rtb.SelectedItems[0].SubItems[1].Text;
            Clipboard.SetText(returnString);
        } 

        #endregion










    }
}
